/****************************************
*
*	EoSC_Weapon
*	
*	Base Weapon class (from here we extend other weapons and types).
*
*	I have taken this from the "Eat3D UnrealScript Masterclass Part1 DVD"
*	by James Tan, Digital Confectioners 2012
*	I highly recommend buying it, especially if you plan to make a Class based game!
*	http://eat3d.com/unrealscript_mc1
*
*	ToDo:
*	Think it would be better to come up from UDKWeapon rather than Weapon.
*	Needs to be archetypable.
*
****************************************/
class EoSC_Weapon extends UDKWeapon
	HideCategories(Movement, Display, Attachment, Collision, Physics, Advanced, Debug, Object);

// Weapon fire modes
var(Weapon) const editinline instanced array<EoSC_WeaponFireMode> FireModes;
// Crosshair texture
var(Crosshair) const Texture2D CrosshairTexture;
// Crosshair relative size 
var(Crosshair) const float CrosshairRelativeSize;
// Crosshair UV coordinates
var(Crosshair) const float CrosshairU;
var(Crosshair) const float CrosshairV;
var(Crosshair) const float CrosshairUL;
var(Crosshair) const float CrosshairVL;
// Crosshair color
var(Crosshair) const Color CrosshairColor;
// Sound to play if the crosshair targets another pawn
var(Crosshair) const SoundCue CrosshairTargetLockSoundCue;

// Last crosshair target
var ProtectedWrite Actor LastCrosshairTargetLock;

/**
 * Called when the weapon is first initialized
 *
 * @network			Server and client
 */
simulated event PostBeginPlay()
{
	local int i;

	Super.PostBeginPlay();

	// Set all of the weapon fire mode object's owner
	if (FireModes.Length > 0)
	{
		for (i = 0; i < FireModes.Length; ++i)
		{
			if (FireModes[i] != None)
			{
				FireModes[i].SetOwner(Self);
			}
		}
	}
}

/**
 * Renders the crosshair for the weapon.
 *
 * @param		HUD			HUD to render onto
 * @network					Server and client
 */
simulated function RenderCrosshair(HUD HUD)
{
	local float CrosshairSize;
	local Vector HitLocation, HitNormal, SocketLocation;
	local Rotator SocketRotation;
	local Actor HitActor;
	local SkeletalMeshComponent SkeletalMeshComponent;
	local Pawn HitPawn;

	// Check all crosshair parameters
	if (HUD == None || HUD.Canvas == None || CrosshairTexture == None || CrosshairRelativeSize <= 0.f || CrosshairUL <= 0.f || CrosshairVL <= 0.f || CrosshairColor.A == 0)
	{
		return;
	}

	// Calculate the crosshair size
	CrosshairSize = CrosshairRelativeSize * HUD.SizeX;

	// Check to see if the weapon can perform a world trace to find the true cross hair location
	SkeletalMeshComponent = SkeletalMeshComponent(Mesh);
	if (SkeletalMeshComponent != None && FireModes.Length > 0 && FireModes[0] != None && SkeletalMeshComponent.GetSocketByName(FireModes[0].FireSocketName) != None)
	{
		// Trace out to find if the crosshair will target something
		SkeletalMeshComponent.GetSocketWorldLocationAndRotation(FireModes[0].FireSocketName, SocketLocation, SocketRotation);
		HitActor = Trace(HitLocation, HitNormal, SocketLocation + Vector(SocketRotation) * 16384.f, SocketLocation, true,,, TRACEFLAG_Bullet);
		if (HitActor != None)
		{
			// Play the lock sound if this is a new target
			HitPawn = Pawn(HitActor);
			if (CrosshairTargetLockSoundCue != None && HitPawn != None && HitPawn.Health > 0 && LastCrosshairTargetLock != HitActor && Instigator != None)
			{
				Instigator.PlaySound(CrosshairTargetLockSoundCue, true);
				LastCrosshairTargetLock = HitActor;
			}

			// Project the crosshair trace hit location to screen space coordinates
			HitLocation = HUD.Canvas.Project(HitLocation);
		}
		else
		{
			// Project the crosshair trace end to screen space coordinates
			HitLocation = HUD.Canvas.Project(SocketLocation + Vector(SocketRotation) * 16384.f);
			// Clear the LastCrosshairTargetLock
			LastCrosshairTargetLock = None;
		}

		// Set the rendering position, and center the crosshair
		HUD.Canvas.SetPos(HitLocation.X - (CrosshairSize * 0.5f), HitLocation.Y - (CrosshairSize * 0.5f));
	}
	// Otherwise just center the crosshair
	else
	{
		HUD.Canvas.SetPos((HUD.SizeX * 0.5f) - (CrosshairSize * 0.5f), (HUD.SizeY * 0.5f) - (CrosshairSize * 0.5f));
	}

	// Set the rendering color
	HUD.Canvas.DrawColor = CrosshairColor;
	// Render the crosshair texture
	HUD.Canvas.DrawTile(CrosshairTexture, CrosshairSize, CrosshairSize, CrosshairU, CrosshairV, CrosshairUL, CrosshairVL);
}

/**
 * Send weapon to proper firing state and sets the CurrentFireMode.
 *
 * @param		FireModeNum			Fire mode
 * @network							Server and local player
 */
simulated function SendToFiringState(byte FireModeNum)
{
	// make sure fire mode is valid
	if (FireModeNum >= FiringStatesArray.Length)
	{
		return;
	}

	// Set the current fire mode
	SetCurrentFireMode(FireModeNum);
	// transition to firing mode state
	GotoState(FiringStatesArray[FireModeNum]);
}

/**
 * Perform all logic associated with firing a shot
 *
 * @network			Server and local player
 */
simulated function FireAmmunition()
{
	// Use ammunition to fire
	ConsumeAmmo(CurrentFireMode);
	
	// Forward the fire call to the FireMode object
	if (CurrentFireMode < FireModes.Length && FireModes[CurrentFireMode] != None)
	{
		FireModes[CurrentFireMode].Fire();
	}

	// Notify that the weapon was fired
	NotifyWeaponFired(CurrentFireMode);
}

/**
 * Main function to play weapon firing effects. This is called from Pawn::WeaponFired in the base implementation.
 *
 * @param		FireModeNum			Fire mode to play effects for
 * @param		HitLocation			Where in the world the weapon hit
 * @network							Server and client
 */
simulated function PlayFireEffects(byte FireModeNum, optional vector HitLocation)
{
	// Forward the play fire effects to the FireMode object
	if (FireModeNum < FireModes.Length && FireModes[FireModeNum] != None)
	{
		FireModes[FireModeNum].PlayFiringEffects(HitLocation);
	}	
}

/**
 * Main function to stop any active effects. This is called from Pawn::WeaponStoppedFiring
 *
 * @param		FireModeNum			Fire mode to stop effects for
 * @network							Server and client
 */
simulated function StopFireEffects(byte FireModeNum)
{
	// Forward the stop fire effects to the FireMode object
	if (FireModeNum < FireModes.Length && FireModes[FireModeNum] != None)
	{
		FireModes[FireModeNum].StopFiringEffects();
	}
}

/**
 * Attaches the weapon to the pawn
 *
 * @param			NewPawn			Pawn to attach the weapon to
 * @network							Server and client
 */
simulated function AttachToPawn(Pawn NewPawn)
{	
	local EoSC_Pawn EoSC_Pawn;

	// Check the mesh and the pawn's mesh
	if (Mesh != None && NewPawn != None && NewPawn.Mesh != None)
	{
		EoSC_Pawn = EoSC_Pawn(NewPawn);
		if (EoSC_Pawn != None && EoSC_Pawn.Mesh.GetSocketByName(EoSC_Pawn.WeaponSocketName) != None)
		{
			// Attach the weapon mesh to the instigator's skeletal mesh
			EoSC_Pawn.Mesh.AttachComponentToSocket(Mesh, EoSC_Pawn.WeaponSocketName);
			// Set the weapon mesh's light environment
			Mesh.SetLightEnvironment(EoSC_Pawn.LightEnvironment);
			// Set the weapon's shadow parent to the instigator's skeletal mesh
			Mesh.SetShadowParent(EoSC_Pawn.Mesh);
			// Set the weapon attachment archetype so that other clients can see what gun this pawn is carrying
			EoSC_Pawn.WeaponAttachmentArchetype = EoSC_Weapon(ObjectArchetype);
		}
	}
}

/**
 * The weapon is in this state while transitioning from Inactive to Active state. Typically, the weapon will remain in this state while its selection animation is being played.
 * While in this state, the weapon cannot be fired.
 *
 * @network			Server and client
 */
simulated state WeaponEquipping
{
	/**
	 * Called when the weapon has finished equipping
	 *
	 * @network			Server and client
	 */
	simulated function WeaponEquipped()
	{
		if (bWeaponPutDown)
		{
			// if switched to another weapon, put down right away
			PutDownWeapon();
			return;
		}

		// Attach the weapon to the instigator's mesh
		AttachToPawn(Instigator);
		GotoState('Active');
	}
}

/**
 * Sets the timing for the firing state on server and local client. By default, a constant looping Rate Of Fire (ROF) is set up. When the delay has expired, the RefireCheckTimer event is triggered.
 *
 * @param			FireModeNum			Fire mode
 * @network								Server and local player
 */
simulated function TimeWeaponFiring(byte FireModeNum)
{
	// If the weapon is a continous fire mode (since it requires a tick during fire) then don't start the refire timer 
	if (CurrentFireMode < FireModes.Length && FireModes[CurrentFireMode] != None && FireModes[CurrentFireMode].RequiredTickDuringFire)
	{
		return;
	}
	
	// Otherwise do the default behavior
	Super.TimeWeaponFiring(FireModeNum);
}

/**
 * The weapon in this state is firing the weapon.
 *
 * @network			Server and client
 */
simulated state WeaponFiring
{
	/**
	 * Every time the weapon is updated
	 *
	 * @param		DeltaTime		Time, in seconds, since the last update
	 * @network						Server and client
	 */
	simulated function Tick(float DeltaTime)
	{
		Global.Tick(DeltaTime);

		// If the fire mode requires tick, then forward the tick event if the player is still firing the weapon
		if (CurrentFireMode < FireModes.Length && FireModes[CurrentFireMode] != None && FireModes[CurrentFireMode].RequiredTickDuringFire)
		{
			if (ShouldRefire())
			{
				FireModes[CurrentFireMode].Tick(DeltaTime);
			}
			// Player no lonegr wants to fire the weapon, handle finished firing
			else
			{
				HandleFinishedFiring();
			}
		}
	}

	/**
	 * Called when the weapon exits this state
	 *
	 * @param		NextStateName			The name of the next state that this weapon will go to
	 * @network								Server and client
	 */
	simulated function EndState(Name NextStateName)
	{
		local int i;

		Super.EndState(NextStateName);

		// Stop the firing effects for the weapon
		if (FireModes.Length > 0)
		{
			for (i = 0; i < FireModes.Length; ++i)
			{
				if (FireModes[i] != None)
				{
					FireModes[i].StopFiringEffects();
				}
			}
		}
	}
}

defaultproperties
{
	Begin Object Class=SkeletalMeshComponent Name=MySkeletalMeshComponent
		bUpdateSkelWhenNotRendered=false
		bIgnoreControllersWhenNotRendered=true
		bAcceptsDynamicDecals=false
		CastShadow=true
		TickGroup=TG_DuringASyncWork
	End Object
	Mesh=MySkeletalMeshComponent
	Components.Add(MySkeletalMeshComponent)

	FiringStatesArray(0)="WeaponFiring"
	FiringStatesArray(1)="WeaponFiring"	
	CrosshairColor=(R=255,G=255,B=255,A=191)
}